package constructor;

import ga.GA;
import ga.GA.Cuerpo;
import ga.GA.Declaracion;
import ga.GA.Declaraciones;
import ga.GA.Exp0;
import ga.GA.Exp1;
import ga.GA.Exp2;
import ga.GA.Exp3;
import ga.GA.Exp4;
import ga.GA.Instruccion;
import ga.GA.Instrucciones;
import ga.GA.ListaDeclaraciones;
import ga.GA.ListaParametrosFormales;
import ga.GA.ListaParametrosReales;
import ga.GA.OpAditivo;
import ga.GA.OpComparacion;
import ga.GA.OpMultiplicativo;
import ga.GA.OpUnario;
import ga.GA.ParametroFormal;
import ga.GA.ParametrosFormales;
import ga.GA.ParametrosReales;
import ga.GA.ParteElse;
import ga.GA.Programa;

import java.io.IOException;

import lexico.AnalizadorLexico;
import lexico.CatLexica;
import utils.Token;

public class ConstructorArboles {

	private Token tact;
	private AnalizadorLexico analizadorLexico;
	private GA gA = new GA();
	private static final boolean DEBUG = true;

	public ConstructorArboles(AnalizadorLexico analizadorLexico) {
		this.analizadorLexico = analizadorLexico;
	}

	public Programa parse() throws IOException {
		tact = analizadorLexico.nextToken();
		Programa aPrograma = recPrograma();
		rec(CatLexica.EOF);
		return aPrograma;
	}

	private Programa recPrograma() throws IOException {
		/**
		 * Programa ::= Declaraciones Cuerpo
		 */
		return programaR1(recDeclaraciones(), recCuerpo());
	}

	private Declaraciones recDeclaraciones() throws IOException {
		/**
		 * Declaraciones ::= decs ListaDeclaraciones end decs
		 * Declaraciones ::=  LAMBDA
		 */
		if (tokenActual(CatLexica.DECS)) {
			rec(CatLexica.DECS);
			ListaDeclaraciones lista = recListaDeclaraciones();
			rec(CatLexica.END_DECS);
			return decsR1(lista);
		} else {
			return decsR2();
		}
	}

	private ListaDeclaraciones recListaDeclaraciones() throws IOException {
		/**
		 * ListaDeclaraciones ::= Declaracion RListaDeclaraciones
		 */
		return recRListaDeclaraciones(lDsR2(recDeclaracion()));
	}

	private ListaDeclaraciones recRListaDeclaraciones(ListaDeclaraciones lista) throws IOException {
		/**
		 * RListaDeclaraciones ::= , Declaracion RListaDeclaraciones
		 * RListaDeclaraciones ::= LAMBDA
		 */
		while (tokenActual(CatLexica.COMA)) {
			rec(CatLexica.COMA);
			lista = lDsR1(lista, recDeclaracion());
		}
		return lista;
	}

	private Declaracion recDeclaracion() throws IOException {
		/**
		 * Declaracion ::= IDEN
		 * Declaracion ::=  proc IDEN ( ParametrosFormales ) Declaraciones Cuerpo
		 */
		if (tokenActual(CatLexica.IDEN)) {
			Token tIden = tact;
			rec(CatLexica.IDEN);
			return decR1(tIden);
		} else if (tokenActual(CatLexica.PROC)) {
			rec(CatLexica.PROC);
			Token tIden = tact;
			if (tokenActual(CatLexica.IDEN)) { 
				rec(CatLexica.IDEN);
				if (tokenActual(CatLexica.PARENTESIS_APERTURA)) {
					rec(CatLexica.PARENTESIS_APERTURA);
					ParametrosFormales pF = recParametrosFormales();
					if (tokenActual(CatLexica.PARENTESIS_CIERRE)) {
						rec(CatLexica.PARENTESIS_CIERRE);
						return decR2(tIden, pF, recDeclaraciones(), recCuerpo());
					} else {
						errorSintactico(CatLexica.PARENTESIS_CIERRE);
						return null;
					}
				} else {
					errorSintactico(CatLexica.PARENTESIS_APERTURA);
					return null;
				}
			} else {
				errorSintactico(CatLexica.IDEN);
				return null;
			}
		} else {
			errorSintactico(CatLexica.IDEN, CatLexica.PROC);
			return null;
		}
	}

	private ParametrosFormales recParametrosFormales() throws IOException {
		/**
		 * ParametrosFormales ::= ListaParametrosFormales
		 * ParametrosFormales ::= LAMBDA
		 */
		if (tokenActual(CatLexica.IDEN, CatLexica.VAR)) {
			return pFsR1(recListaParametrosFormales());
		} else {
			return pFsR2();
		}
	}

	private ListaParametrosFormales recListaParametrosFormales() throws IOException {
		/**
		 * ListaParametrosFormales ::= ParametroFormal RListaParametrosFormales
		 */
		return recRListaParametrosFormales(lPFsR2(recParametroFormal()));
	}

	private ListaParametrosFormales recRListaParametrosFormales(ListaParametrosFormales lista) throws IOException {
		/**
		 * RListaParametrosFormales ::= , ParametroFormal RListaParametrosFormales
		 * RListaParametrosFormales ::= LAMBDA
		 */
		while(tokenActual(CatLexica.COMA)) {
			rec(CatLexica.COMA);
			lista = lPFsR1(lista, recParametroFormal());
		}
		return lista;
	}

	private ParametroFormal recParametroFormal() throws IOException {
		/**
		 * ParametroFormal ::= IDEN
		 * ParametroFormal ::= var IDEN
		 */
		if (tokenActual(CatLexica.VAR)) {
			rec(CatLexica.VAR);
			if (tokenActual(CatLexica.IDEN)) { 
				Token tIden = tact;
				rec(CatLexica.IDEN);
				return pFR2(tIden);
			} else {
				errorSintactico(CatLexica.IDEN);
				return null;
			}
		} else if (tokenActual(CatLexica.IDEN)) {
			Token tIden = tact;
			rec(CatLexica.IDEN);
			return pFR1(tIden);
		} else {
			errorSintactico(CatLexica.IDEN, CatLexica.VAR);
			return null;
		}
	}

	private Cuerpo recCuerpo() throws IOException {
		/**
		 * Cuerpo ::= body Instrucciones end body
		 */
		if (tokenActual(CatLexica.BODY)) {
			rec(CatLexica.BODY);
			Instrucciones insts = recInstrucciones();
			if (tokenActual(CatLexica.END_BODY)) {
				rec(CatLexica.END_BODY);
				return cuerpoR1(insts);
			} else {
				errorSintactico(CatLexica.END_BODY);
				return null;
			}
		} else {
			errorSintactico(CatLexica.BODY);
			return null;
		}
	}

	private Instrucciones recInstrucciones() throws IOException {
		/**
		 * Instrucciones ::= Instruccion RInstrucciones
		 */
		return recRInstrucciones(instsR2(recInstruccion()));
	}

	private Instrucciones recRInstrucciones(Instrucciones insts) throws IOException {
		/**
		 * RInstrucciones ::= , Instruccion RInstrucciones
		 * RInstrucciones ::= LAMBDA
		 */
		while(tokenActual(CatLexica.COMA)) {
			rec(CatLexica.COMA);
			insts = instsR1(insts, recInstruccion());
		}
		return insts;
	}

	private Instruccion recInstruccion() throws IOException {
		/**
		 * Instruccion ::= set IDEN to Exp0
		 * Instruccion ::= call IDEN ParametrosReales end call
		 * Instruccion ::= if Exp0 then {Instrucciones} ParteElse
		 * Instruccion ::= out IDEN
		 */
		if (tokenActual(CatLexica.SET)) {
			rec(CatLexica.SET);
			if (tokenActual(CatLexica.IDEN)) { 
				Token tIden = tact;
				rec(CatLexica.IDEN);
				if (tokenActual(CatLexica.TO)) { 
					rec(CatLexica.TO);
					return instR1(tIden, recExp0());
				} else {
					errorSintactico(CatLexica.TO);
					return null;
				}
			} else {
				errorSintactico(CatLexica.IDEN);
				return null;
			}
		} else if (tokenActual(CatLexica.CALL)) {
			rec(CatLexica.CALL);
			if (tokenActual(CatLexica.IDEN)) { 
				Token tIden = tact;
				rec(CatLexica.IDEN);
				ParametrosReales pRs = recParametrosReales();
				if (tokenActual(CatLexica.END_CALL)) {
					rec(CatLexica.END_CALL);
					return instR2(tIden, pRs);
				} else {
					errorSintactico(CatLexica.END_CALL);
					return null;
				}
			} else {
				errorSintactico(CatLexica.IDEN);
				return null;
			}
		} else if (tokenActual(CatLexica.IF)) {
			rec(CatLexica.IF);
			Exp0 exp0 = recExp0();
			if (tokenActual(CatLexica.THEN)) { 
				rec(CatLexica.THEN);
				if (tokenActual(CatLexica.LLAVE_APERTURA)) {
					rec(CatLexica.LLAVE_APERTURA);
					Instrucciones insts = recInstrucciones();
					if (tokenActual(CatLexica.LLAVE_CIERRE)) {
						rec(CatLexica.LLAVE_CIERRE);
						return instR3(exp0, insts, recParteElse());
					} else {
						errorSintactico(CatLexica.LLAVE_CIERRE);
						return null;
					}
				} else {
					errorSintactico(CatLexica.LLAVE_APERTURA);
					return null;
				}
			} else {
				errorSintactico(CatLexica.THEN);
				return null;
			}
		} else if (tokenActual(CatLexica.OUT)) {
			rec(CatLexica.OUT);
			if (tokenActual(CatLexica.IDEN)) { 
				Token tIden = tact;
				rec(CatLexica.IDEN);
				return instR4(tIden);
			} else {
				errorSintactico(CatLexica.IDEN);
				return null;
			}
		} else {
			errorSintactico(CatLexica.SET, CatLexica.CALL, CatLexica.IF, CatLexica.OUT);
			return null;
		}
	}

	private ParametrosReales recParametrosReales() throws IOException {
		/**
		 * ParametrosReales ::= with ListaParametrosReales
		 * ParametrosReales ::= LAMBDA
		 */
		if (tokenActual(CatLexica.WITH)) {
			rec(CatLexica.WITH);
			return pRsR1(recListaParametrosReales());
		} else {
			return pRsR2();
		}
	}

	private ListaParametrosReales recListaParametrosReales() throws IOException {
		/**
		 * ListaParametrosReales ::= Exp0 RListaParametrosReales
		 */
		return recRListaParametrosReales(lPRsR2(recExp0()));
	}

	private ListaParametrosReales recRListaParametrosReales(ListaParametrosReales lista) throws IOException {
		/**
		 * RListaParametrosReales ::= , Exp0 RListaParametrosReales
		 * RListaParametrosReales ::= LAMBDA
		 */
		while (tokenActual(CatLexica.COMA)) {
			rec(CatLexica.COMA);
			lista = lPRsR1(lista, recExp0());
		}
		return lista;
	}

	private ParteElse recParteElse() throws IOException {
		/**
		 * ParteElse ::= else {Instrucciones}
		 * ParteElse ::= LAMBDA
		 */
		if (tokenActual(CatLexica.ELSE)) {
			rec(CatLexica.ELSE);
			if (tokenActual(CatLexica.LLAVE_APERTURA)) {
				rec(CatLexica.LLAVE_APERTURA);
				Instrucciones insts = recInstrucciones();
				if (tokenActual(CatLexica.LLAVE_CIERRE)) {
					rec(CatLexica.LLAVE_CIERRE);
					return elseR1(insts);
				} else {
					errorSintactico(CatLexica.LLAVE_CIERRE);
					return null;
				}
			} else {
				errorSintactico(CatLexica.LLAVE_APERTURA);
				return null;
			}
		} else {
			return elseR2();
		}
	}

	private Exp0 recExp0() throws IOException {
		/**
		 * Exp0 ::= Exp1 RExp0
		 */
		return recRExp0(recExp1());
	}
	
	private Exp0 recRExp0(Exp1 exp1) throws IOException {
		/**
		 * RExp0 ::= OpComparacion Exp1
		 * RExp0 ::= LAMBDA
		 */
		if (esOpComparacion()) {
			return exp0R1(exp1, recOpComparacion(), recExp1());
		} else {
			return exp0R2(exp1);
		}
	}

	private Exp1 recExp1() throws IOException {
		/**
		 * Exp1 ::= Exp2 RExp1
		 */
		return recRExp1(exp1R2(recExp2()));
	}

	private Exp1 recRExp1(Exp1 exp1) throws IOException {
		/**
		 * RExp1 ::= OpAditivo Exp2 RExp1
		 * RExp1 ::= LAMBDA
		 */
		while (esOpAditivo()) {
			exp1 = exp1R1(exp1, recOpAditivo(), recExp2());
		}
		return exp1;
	}

	private Exp2 recExp2() throws IOException {
		/**
		 * Exp2 ::= Exp3 RExp2
		 */
		return recRExp2(exp2R2(recExp3()));
	}

	private Exp2 recRExp2(Exp2 exp2) throws IOException {
		/**
		 * RExp2 ::= OpMultiplicativo Exp3 RExp2
		 * RExp2 ::= LAMBDA
		 */
		while (esOpMultiplicativo()) {
			exp2 = exp2R1(exp2, recOpMultiplicativo(), recExp3());
		}
		return exp2;
	}

	private Exp3 recExp3() throws IOException {
		/**
		 * Exp3 ::= OpUnario Exp3
		 * Exp3 ::= Exp4
		 */
		if (esOpUnario()) {
			return exp3R1(recOpUnario(), recExp3());
		} else {
			return exp3R2(recExp4());
		}
	}

	private Exp4 recExp4() throws IOException {
		/**
		 * Exp4 ::= NUM
		 * Exp4 ::= IDEN
		 * Exp4 ::= ( Exp0 )
		 */
		if (tokenActual(CatLexica.NUM)) {
			Token tNum = tact;
			rec(CatLexica.NUM);
			return exp4R1(tNum);
		} else if (tokenActual(CatLexica.IDEN)) {
			Token tIden = tact;
			rec(CatLexica.IDEN);
			return exp4R2(tIden);
		} else if (tokenActual(CatLexica.PARENTESIS_APERTURA)) {
			rec(CatLexica.PARENTESIS_APERTURA);
			Exp0 exp0 = recExp0();
			if (tokenActual(CatLexica.PARENTESIS_CIERRE)) {
				rec(CatLexica.PARENTESIS_CIERRE);
				return exp4R3(exp0);
			} else {
				errorSintactico(CatLexica.PARENTESIS_CIERRE);
				return null;
			}
		} else {
			errorSintactico(CatLexica.NUM, CatLexica.IDEN,
					CatLexica.PARENTESIS_APERTURA);
			return null;
		}
	}

	private OpComparacion recOpComparacion() throws IOException {
		/**
		 * OpComparacion ::= =
		 * OpComparacion ::= =/=
		 * OpComparacion ::= >
		 * OpComparacion ::= >=
		 * OpComparacion ::= <
		 * OpComparacion ::= <=
		 */
		if (tokenActual(CatLexica.IGUAL)) {
			rec(CatLexica.IGUAL);
			return opComparacionR1();
		} else if (tokenActual(CatLexica.DISTINTO)) {
			rec(CatLexica.DISTINTO);
			return opComparacionR2();
		} else if (tokenActual(CatLexica.MAYOR)) {
			rec(CatLexica.MAYOR);
			return opComparacionR3();
		} else if (tokenActual(CatLexica.MAYOR_IGUAL)) {
			rec(CatLexica.MAYOR_IGUAL);
			return opComparacionR4();
		} else if (tokenActual(CatLexica.MENOR)) {
			rec(CatLexica.MENOR);
			return opComparacionR5();
		} else if (tokenActual(CatLexica.MENOR_IGUAL)) {
			rec(CatLexica.MENOR_IGUAL);
			return opComparacionR6();
		} else {
			errorSintactico(CatLexica.IGUAL, CatLexica.DISTINTO,
					CatLexica.MAYOR, CatLexica.MAYOR_IGUAL, CatLexica.MENOR,
					CatLexica.MENOR_IGUAL);
			return null;
		}
	}

	private OpAditivo recOpAditivo() throws IOException {
		/**
		 * OpAditivo ::= +
		 * OpAditivo ::= -
		 * OpAditivo ::= or
		 */
		if (tokenActual(CatLexica.MAS)) {
			rec(CatLexica.MAS);
			return opAditivoR1();
		} else if (tokenActual(CatLexica.MENOS)) {
			rec(CatLexica.MENOS);
			return opAditivoR2();
		} else if (tokenActual(CatLexica.OR)) {
			rec(CatLexica.OR);
			return opAditivoR3();
		} else {
			errorSintactico(CatLexica.MAS, CatLexica.MENOS, CatLexica.OR);
			return null;
		}
	}

	private OpMultiplicativo recOpMultiplicativo() throws IOException {
		/**
		 * OpMultiplicativo ::= *
		 * OpMultiplicativo ::= /
		 * OpMultiplicativo ::= and
		 */
		if (tokenActual(CatLexica.POR)) {
			rec(CatLexica.POR);
			return opMultiplicativoR1();
		} else if (tokenActual(CatLexica.DIV)) {
			rec(CatLexica.DIV);
			return opMultiplicativoR2();
		} else if (tokenActual(CatLexica.AND)) {
			rec(CatLexica.AND);
			return opMultiplicativoR3();
		} else {
			errorSintactico(CatLexica.POR, CatLexica.DIV, CatLexica.AND);
			return null;
		}
	}

	private OpUnario recOpUnario() throws IOException {
		/**
		 * OpUnario ::= -
		 * OpUnario ::= not
		 */
		if (tokenActual(CatLexica.MENOS)) {
			rec(CatLexica.MENOS);
			return opUnarioR1();
		} else if (tokenActual(CatLexica.NOT)) {
			rec(CatLexica.NOT);
			return opUnarioR2();
		} else {
			errorSintactico(CatLexica.MENOS, CatLexica.NOT);
			return null;
		}
	}

	// REGLAS

	private Programa programaR1(Declaraciones decs, Cuerpo cuerpo) {
		if (DEBUG)
			return gA.new ProgramaR1Debug(decs, cuerpo);
		else
			return gA.new ProgramaR1(decs, cuerpo);
	}
	
	private Declaraciones decsR1(ListaDeclaraciones lista) {
		if (DEBUG)
			return gA.new DeclaracionesR1Debug(lista);
		else
			return gA.new DeclaracionesR1(lista);
	}

	private Declaraciones decsR2() {
		if (DEBUG)
			return gA.new DeclaracionesR2Debug();
		else
			return gA.new DeclaracionesR2();
	}
	
	private ListaDeclaraciones lDsR1(ListaDeclaraciones lista,
			Declaracion dec) {
		if (DEBUG)
			return gA.new ListaDeclaracionesR1Debug(lista, dec);
		else
			return gA.new ListaDeclaracionesR1(lista, dec);
	}
	
	private ListaDeclaraciones lDsR2(Declaracion dec) {
		if (DEBUG)
			return gA.new ListaDeclaracionesR2Debug(dec);
		else
			return gA.new ListaDeclaracionesR2(dec);
	}
	
	private Declaracion decR1(Token id) {
		if (DEBUG)
			return gA.new DeclaracionR1Debug(id);
		else
			return gA.new DeclaracionR1(id);
	}

	private Declaracion decR2(Token id, ParametrosFormales pFs,
			Declaraciones decs, Cuerpo cuerpo) {
		if (DEBUG)
			return gA.new DeclaracionR2Debug(id, pFs, decs, cuerpo);
		else
			return gA.new DeclaracionR2(id, pFs, decs, cuerpo);
	}
	
	private ParametrosFormales pFsR1(ListaParametrosFormales lPFs) {
		if (DEBUG)
			return gA.new ParametrosFormalesR1Debug(lPFs);
		else
			return gA.new ParametrosFormalesR1(lPFs);
	}

	private ParametrosFormales pFsR2() {
		if (DEBUG)
			return gA.new ParametrosFormalesR2Debug();
		else
			return gA.new ParametrosFormalesR2();
	}
	
	private ListaParametrosFormales lPFsR1(ListaParametrosFormales lista,
			ParametroFormal pF) {
		if (DEBUG)
			return gA.new ListaParametrosFormalesR1Debug(lista, pF);
		else
			return gA.new ListaParametrosFormalesR1(lista, pF);
	}
	
	private ListaParametrosFormales lPFsR2(ParametroFormal pF) {
		if (DEBUG)
			return gA.new ListaParametrosFormalesR2Debug(pF);
		else
			return gA.new ListaParametrosFormalesR2(pF);
	}
	
	private ParametroFormal pFR1(Token id) {
		if (DEBUG)
			return gA.new ParametroFormalR1Debug(id);
		else
			return gA.new ParametroFormalR1(id);
	}

	private ParametroFormal pFR2(Token id) {
		if (DEBUG)
			return gA.new ParametroFormalR2Debug(id);
		else
			return gA.new ParametroFormalR2(id);
	}
	
	private Cuerpo cuerpoR1(Instrucciones insts) {
		if (DEBUG)
			return gA.new CuerpoR1Debug(insts);
		else
			return gA.new CuerpoR1(insts);
	}
	
	private Instrucciones instsR1(Instrucciones insts, Instruccion inst) {
		if (DEBUG)
			return gA.new InstruccionesR1Debug(insts, inst);
		else
			return gA.new InstruccionesR1(insts, inst);
	}
	
	private Instrucciones instsR2(Instruccion inst) {
		if (DEBUG)
			return gA.new InstruccionesR2Debug(inst);
		else
			return gA.new InstruccionesR2(inst);
	}
	
	private Instruccion instR1(Token id, Exp0 exp0) {
		if (DEBUG)
			return gA.new InstruccionR1Debug(id, exp0);
		else
			return gA.new InstruccionR1(id, exp0);
	}

	private Instruccion instR2(Token id, ParametrosReales pRs) {
		if (DEBUG)
			return gA.new InstruccionR2Debug(id, pRs);
		else
			return gA.new InstruccionR2(id, pRs);
	}

	private Instruccion instR3(Exp0 exp0, Instrucciones insts,
			ParteElse pElse) {
		if (DEBUG)
			return gA.new InstruccionR3Debug(exp0, insts, pElse);
		else
			return gA.new InstruccionR3(exp0, insts, pElse);
	}

	private Instruccion instR4(Token id) {
		if (DEBUG)
			return gA.new InstruccionR4Debug(id);
		else
			return gA.new InstruccionR4(id);
	}
	
	private ParametrosReales pRsR1(ListaParametrosReales lPRs) {
		if (DEBUG)
			return gA.new ParametrosRealesR1Debug(lPRs);
		else
			return gA.new ParametrosRealesR1(lPRs);
	}

	private ParametrosReales pRsR2() {
		if (DEBUG)
			return gA.new ParametrosRealesR2Debug();
		else
			return gA.new ParametrosRealesR2();
	}

	private ListaParametrosReales lPRsR1(ListaParametrosReales lista,
			Exp0 exp0) {
		if (DEBUG)
			return gA.new ListaParametrosRealesR1Debug(lista, exp0);
		else
			return gA.new ListaParametrosRealesR1(lista, exp0);
	}
	
	private ListaParametrosReales lPRsR2(Exp0 exp0) {
		if (DEBUG)
			return gA.new ListaParametrosRealesR2Debug(exp0);
		else
			return gA.new ListaParametrosRealesR2(exp0);
	}
	
	private ParteElse elseR1(Instrucciones insts) {
		if (DEBUG)
			return gA.new ParteElseR1Debug(insts);
		else
			return gA.new ParteElseR1(insts);
	}

	private ParteElse elseR2() {
		if (DEBUG)
			return gA.new ParteElseR2Debug();
		else
			return gA.new ParteElseR2();
	}
	
	private Exp0 exp0R1(Exp1 exp10, OpComparacion op, Exp1 exp11) {
		if (DEBUG)
			return gA.new Exp0R1Debug(exp10, op, exp11);
		else
			return gA.new Exp0R1(exp10, op, exp11);
	}

	private Exp0 exp0R2(Exp1 exp1) {
		if (DEBUG)
			return gA.new Exp0R2Debug(exp1);
		else
			return gA.new Exp0R2(exp1);
	}
	
	private Exp1 exp1R1(Exp1 exp1, OpAditivo op, Exp2 exp2) {
		if (DEBUG)
			return gA.new Exp1R1Debug(exp1, op, exp2);
		else
			return gA.new Exp1R1(exp1, op, exp2);
	}
	
	private Exp1 exp1R2(Exp2 exp2) {
		if (DEBUG)
			return gA.new Exp1R2Debug(exp2);
		else
			return gA.new Exp1R2(exp2);
	}
	
	private Exp2 exp2R1(Exp2 exp2, OpMultiplicativo op, Exp3 exp3) {
		if (DEBUG)
			return gA.new Exp2R1Debug(exp2, op, exp3);
		else
			return gA.new Exp2R1(exp2, op, exp3);
	}
	
	private Exp2 exp2R2(Exp3 exp3) {
		if (DEBUG)
			return gA.new Exp2R2Debug(exp3);
		else
			return gA.new Exp2R2(exp3);
	}
	
	private Exp3 exp3R1(OpUnario op, Exp3 exp3) {
		if (DEBUG)
			return gA.new Exp3R1Debug(op, exp3);
		else
			return gA.new Exp3R1(op, exp3);
	}

	private Exp3 exp3R2(Exp4 exp4) {
		if (DEBUG)
			return gA.new Exp3R2Debug(exp4);
		else
			return gA.new Exp3R2(exp4);
	}
	
	private Exp4 exp4R1(Token num) {
		if (DEBUG)
			return gA.new Exp4R1Debug(num);
		else
			return gA.new Exp4R1(num);
	}

	private Exp4 exp4R2(Token id) {
		if (DEBUG)
			return gA.new Exp4R2Debug(id);
		else
			return gA.new Exp4R2(id);
	}

	private Exp4 exp4R3(Exp0 exp0) {
		if (DEBUG)
			return gA.new Exp4R3Debug(exp0);
		else
			return gA.new Exp4R3(exp0);
	}
	
	private OpComparacion opComparacionR1() {
		if (DEBUG)
			return gA.new OpComparacionR1Debug();
		else
			return gA.new OpComparacionR1();
	}

	private OpComparacion opComparacionR2() {
		if (DEBUG)
			return gA.new OpComparacionR2Debug();
		else
			return gA.new OpComparacionR2();
	}

	private OpComparacion opComparacionR3() {
		if (DEBUG)
			return gA.new OpComparacionR3Debug();
		else
			return gA.new OpComparacionR3();
	}

	private OpComparacion opComparacionR4() {
		if (DEBUG)
			return gA.new OpComparacionR4Debug();
		else
			return gA.new OpComparacionR4();
	}

	private OpComparacion opComparacionR5() {
		if (DEBUG)
			return gA.new OpComparacionR5Debug();
		else
			return gA.new OpComparacionR5();
	}

	private OpComparacion opComparacionR6() {
		if (DEBUG)
			return gA.new OpComparacionR6Debug();
		else
			return gA.new OpComparacionR6();
	}
	
	private OpAditivo opAditivoR1() {
		if (DEBUG)
			return gA.new OpAditivoR1Debug();
		else
			return gA.new OpAditivoR1();
	}

	private OpAditivo opAditivoR2() {
		if (DEBUG)
			return gA.new OpAditivoR2Debug();
		else
			return gA.new OpAditivoR2();
	}

	private OpAditivo opAditivoR3() {
		if (DEBUG)
			return gA.new OpAditivoR3Debug();
		else
			return gA.new OpAditivoR3();
	}
	
	private OpMultiplicativo opMultiplicativoR1() {
		if (DEBUG)
			return gA.new OpMultiplicativoR1Debug();
		else
			return gA.new OpMultiplicativoR1();
	}

	private OpMultiplicativo opMultiplicativoR2() {
		if (DEBUG)
			return gA.new OpMultiplicativoR2Debug();
		else
			return gA.new OpMultiplicativoR2();
	}

	private OpMultiplicativo opMultiplicativoR3() {
		if (DEBUG)
			return gA.new OpMultiplicativoR3Debug();
		else
			return gA.new OpMultiplicativoR3();
	}
	
	private OpUnario opUnarioR1() {
		if (DEBUG)
			return gA.new OpUnarioR1Debug();
		else
			return gA.new OpUnarioR1();
	}

	private OpUnario opUnarioR2() {
		if (DEBUG)
			return gA.new OpUnarioR2Debug();
		else
			return gA.new OpUnarioR2();
	}

	// AUXILIARES
	
	private void rec(CatLexica c) throws IOException {
		if (tokenActual(c)) {
			tact = analizadorLexico.nextToken();
		} else {
			errorSintactico(c);
		}
	}

	private void errorSintactico(CatLexica... esperadas) {
		System.err.print("(" + tact.getFila() + "," + tact.getCol() + ")"
				+ "ERROR SINTACTICO: Encontrado " + tact
				+ ". Se esperaba alguno de los siguientes elementos: ");
		for (CatLexica catEsperada : esperadas) {
			System.err.print(catEsperada + " ");
		}
		System.err.println();
		System.exit(1);
	}

	private boolean tokenActual(CatLexica... c) {
		boolean encontrada = false;
		int i = 0;
		while (i < c.length && !encontrada) {
			encontrada = tact.getCategoria() == c[i];
			i++;
		}
		return encontrada;
	}

	@SuppressWarnings("unused")
	private Token nextToken() throws IOException {
		return analizadorLexico.nextToken();
	}
	
	private boolean esOpComparacion() {
		return (tokenActual(CatLexica.IGUAL, CatLexica.DISTINTO,
				CatLexica.MAYOR, CatLexica.MAYOR_IGUAL, CatLexica.MENOR,
				CatLexica.MENOR_IGUAL));
	}

	private boolean esOpAditivo() {
		return (tokenActual(CatLexica.MAS, CatLexica.MENOS, CatLexica.OR));
	}

	private boolean esOpMultiplicativo() {
		return (tokenActual(CatLexica.POR, CatLexica.DIV, CatLexica.AND));
	}

	private boolean esOpUnario() {
		return (tokenActual(CatLexica.MENOS, CatLexica.NOT));
	}

}

